package com.xuan.utils;

public class SnowflakeIdGenerator {
    // 位数分配
    private final long workerId;         // 工作机器ID（10位，最多1024台机器）
    private final long datacenterId;     // 数据中心ID（10位）
    private long sequence = 0L;          // 序列号（12位，每毫秒4096个）
    private long lastTimestamp = -1L;    // 上次生成ID的时间戳
    
    // 位移偏移量
    private static final long WORKER_ID_SHIFT = 12;
    private static final long DATACENTER_ID_SHIFT = 22;
    private static final long TIMESTAMP_SHIFT = 22;
    private static final long SEQUENCE_MASK = 0xFFF;  // 4095
    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        // 校验参数范围
        if (workerId > 1023 || workerId < 0) {
            throw new IllegalArgumentException("Worker ID must be between 0 and 1023");
        }
        if (datacenterId > 1023 || datacenterId < 0) {
            throw new IllegalArgumentException("Datacenter ID must be between 0 and 1023");
        }
    }
    
    // 生成ID的同步方法（避免时钟回拨问题）
    public synchronized long generateId() {
        long timestamp = System.currentTimeMillis();
        
        // 处理时钟回拨
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", 
                lastTimestamp - timestamp));
        }
        
        // 同一毫秒内，序列号自增
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            // 同一毫秒内序列号已用完，等待下一毫秒
            if (sequence == 0) {
                timestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            // 不同毫秒，序列号重置为0
            sequence = 0L;
        }
        
        lastTimestamp = timestamp;
        
        // 按位组合生成ID
        return ((timestamp - 1420041600000L) << TIMESTAMP_SHIFT) |
               (datacenterId << DATACENTER_ID_SHIFT) |
               (workerId << WORKER_ID_SHIFT) |
               sequence;
    }
    
    // 等待下一毫秒
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}